using System;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using NUnit.Framework;
using ValidationFramework.Configuration;
using ValidationFramework.Extensions;
using ValidationFramework.Reflection;

namespace ValidationFramework.Tests.Configuration
{
	public class NoConfigReaderRule : Rule
	{
		public NoConfigReaderRule(string errorMessage, string ruleSet, bool useErrorMessageProvider)
			: base(typeof(string).TypeHandle)
		{
		}

		public override string RuleInterpretation
		{
			get
			{
				throw new NotImplementedException();
			}
		}

		protected override string GetComputedErrorMessage(string tokenizedMemberName, string descriptorType)
		{
			throw new NotImplementedException();
		}

		public override bool Validate(object targetObjectValue, object targetMemberValue, object context, InfoDescriptor infoDescriptor)
		{
			throw new NotImplementedException();
		}

		public override bool IsEquivalent(Rule rule)
		{
			throw new NotImplementedException();
		}

	}

	public class Employee : Person
	{

	}

	public class Person
	{
		private static readonly RuntimeMethodHandle setTheAgeHandle =
			typeof(Person).GetMethod("SetTheAge").MethodHandle;

	    public string Name { get; set; }


	    public int Age { get; set; }


	    public void SetTheAge(int age)
		{
			ParameterValidationManager.ThrowException(this, setTheAgeHandle, age);
			Age = age;
		}


		public void SetTheAge(long age)
		{
			ParameterValidationManager.ThrowException(this, setTheAgeHandle, age);
			Age = (int)age;
		}


		public void SetTheAge2(int age)
		{
			ParameterValidationManager.ThrowException(this, setTheAgeHandle, age);
			Age = age;
		}
	}


	[TestFixture]
	public class ConfigurationServiceTest
	{

		private const string propertyXmlString = @"<?xml version='1.0' encoding='utf-8' ?>
<validationMapping xmlns='urn:validationFramework-validationDefinition-1.5'>
  <class typeName='ValidationFramework.Tests.Configuration.Person, ValidationFramework.Tests'>
    <property name='Name'>
      <rule typeName='RequiredStringRule' initialValue='aaaa'/>
    </property>
    <property name='Age'>
      <rule typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
    </property>
  </class>
</validationMapping>";

		private const string methodXmlString = @"<?xml version='1.0' encoding='utf-8' ?>
<validationMapping xmlns='urn:validationFramework-validationDefinition-1.5'>
  <class typeName='ValidationFramework.Tests.Configuration.Person, ValidationFramework.Tests'>
 
    <method name='SetTheAge'>
      <overloadType typeName='System.Int32'/>
      <parameter name='age'>
        <rule typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleSet='a' typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleSet='b' typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleInterpretation='This is a custom rule' ruleSet='foo' validationMethod='ValidateValid' validationTypeName='ValidationFramework.Tests.CustomRuleTest+ValidationClass,ValidationFramework.Tests' errorMessage='hello' typeName='CustomRule'/>
      </parameter>
    </method>
    <method name='SetTheAge2'>
      <parameter name='age'>
        <rule typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleSet='a' typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleSet='b' typeName='RangeRule' minimum='18' maximum='80' errorMessage='Age is invalid' />
        <rule ruleInterpretation='This is a custom rule' ruleSet='foo' validationMethod='ValidateValid' validationTypeName='ValidationFramework.Tests.CustomRuleTest+ValidationClass,ValidationFramework.Tests' errorMessage='hello' typeName='CustomRule'/>
      </parameter>
    </method>
  </class>
</validationMapping>";



		[TestFixture]
		public class ConfigurationServiceTestBase
		{
			[SetUp]
			public void SetUp()
			{
				TypeCache.Clear();
				MethodCache.Clear();
			}

			[TearDown]
			public void TearDown()
			{

				TypeCache.Clear();
				MethodCache.Clear();
				if (File.Exists("temp.xml"))
				{
					File.Delete("temp.xml");
				}
			}
		}
		public class AddAssembly : ConfigurationServiceTestBase
		{
			[Test]
			public void ByAssembly()
			{
				ConfigurationService.AddAssembly(typeof(ConfigurationServiceTest).Assembly);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}


			[Test]
			public void ByName()
			{
				ConfigurationService.AddAssembly("ValidationFramework.Tests");
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);


			}


			[Test]
			[ExpectedException(typeof(FileNotFoundException))]
			public void ExceptionInvalidName()
			{
				ConfigurationService.AddAssembly("InvalidAssemblyName");
			}

		}

		public class AddClass : ConfigurationServiceTestBase
		{
			[Test]
			public void Simple()
			{
				var type = typeof(Person);
				ConfigurationService.AddClass(type);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}

		}

		public class AddXmlFile : ConfigurationServiceTestBase
		{
			[Test]
			[ExpectedException(typeof(Exception))]
			public void ExceptionNotExist()
			{
				ConfigurationService.AddXmlFile(@"Invalid.xml");
			}

			[Test]
			public void Simple()
			{
				var path = WriteXmlConfig();

				ConfigurationService.AddXmlFile(path);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}


		}
		private static string WriteXmlConfig()
		{
			using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ValidationFramework.Tests.Configuration.Person.validation.xml"))
			{
				using (var streamReader = new StreamReader(stream))
				{
					var fullPath = Path.GetFullPath("temp.xml");
					using (var streamWriter = new StreamWriter(fullPath, false))
					{
						streamWriter.Write(streamReader.ReadToEnd());
					}
					return fullPath;
				}
			}
		}

		public class AddUrl : ConfigurationServiceTestBase
		{
			[Test]
			public void AddUrlValid()
			{
				var path = WriteXmlConfig();
				ConfigurationService.AddUrl(path);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}


			[Test]
			public void AddUriValid()
			{

				var path = WriteXmlConfig();
				ConfigurationService.AddUrl(new Uri(path));
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}
		}

		public class AddDirectory : ConfigurationServiceTestBase
		{

			[Test]
			public void Simple()
			{
				try
				{
					var path = WriteXmlConfig();
					Directory.CreateDirectory("temp");
					File.Move(path, "temp/temp.validation.xml");
					ConfigurationService.AddDirectory(new DirectoryInfo(@"./"));
					var type = typeof(Person);
					var typeDescriptor = TypeCache.GetType(type.TypeHandle);
					Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

					var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
					var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
					Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

					var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
					var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
					Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
				}
				finally
				{
					if (Directory.Exists("temp"))
					{
						if (File.Exists("temp/temp.validation.xml"))
						{
							File.Delete("temp/temp.validation.xml");
							Directory.Delete("temp");
						}
					}
				}

			}
		}

		public class AddXmlString : ConfigurationServiceTestBase
		{

			[Test]
			public void Method()
			{
				ConfigurationService.AddXmlString(methodXmlString);
				var type = typeof(Person);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}


			[Test]
			public void Property()
			{
				ConfigurationService.AddXmlString(propertyXmlString);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(1, typeDescriptor.Properties["Age"].Rules.Count);
			}


			[Test]
			public void PropertySub()
			{
				ConfigurationService.AddXmlString(propertyXmlString);
				var type = typeof(Employee);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(1, typeDescriptor.Properties["Age"].Rules.Count);
			}

		}
#if (!SILVERLIGHT)
		public class AddDocument : ConfigurationServiceTestBase
		{
			[Test]
			public void Method()
			{
				var xmlDocument = new XmlDocument();
				xmlDocument.LoadXml(methodXmlString);
				ConfigurationService.AddDocument(xmlDocument);
				var type = typeof(Person);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}


			[Test]
			public void Property()
			{
				var xmlDocument = new XmlDocument();
				xmlDocument.LoadXml(propertyXmlString);
				ConfigurationService.AddDocument(xmlDocument);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(1, typeDescriptor.Properties["Age"].Rules.Count);
			}

		}
#endif
		public class AddXmlReader : ConfigurationServiceTestBase
		{
			[Test]
			public void Property()
			{
				var xmlTextReader = XmlReader.Create(new StringReader(propertyXmlString));
				ConfigurationService.AddXmlReader(xmlTextReader);
				var type = typeof(Person);
				var typeDescriptor = TypeCache.GetType(type.TypeHandle);
				Assert.AreEqual(1, typeDescriptor.Properties["Age"].Rules.Count);

			}


			[Test]
			public void Method()
			{
				var xmlTextReader = XmlReader.Create(new StringReader(methodXmlString));
				ConfigurationService.AddXmlReader(xmlTextReader);
				var type = typeof(Person);

				var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
				var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
				Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

				var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
				var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
				Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
			}
		}

		public class AddInputStream : ConfigurationServiceTestBase
		{

			[Test]
			public void Simple()
			{
				using (var stream = GetType().Assembly.GetManifestResourceStream("ValidationFramework.Tests.Configuration.Person.validation.xml"))
				{
					ConfigurationService.AddInputStream(stream);
					var type = typeof(Person);
					var typeDescriptor = TypeCache.GetType(type.TypeHandle);
					Assert.AreEqual(4, typeDescriptor.Properties["Age"].Rules.Count);

					var setTheAgeIntMethodHandle = type.GetMethod("SetTheAge", new[] { typeof(int) }).MethodHandle;
					var intSetTheAgeMethodDescriptor = MethodCache.GetMethod(setTheAgeIntMethodHandle);
					Assert.AreEqual(4, intSetTheAgeMethodDescriptor.Parameters["age"].Rules.Count);

					var setTheAge2MethodHandle = type.GetMethod("SetTheAge2").MethodHandle;
					var setTheAge2MethodDescriptor = MethodCache.GetMethod(setTheAge2MethodHandle);
					Assert.AreEqual(4, setTheAge2MethodDescriptor.Parameters["age"].Rules.Count);
				}
			}

		}
		[TestFixture]
		public class GetRule : ConfigurationServiceTestBase
		{
			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not load IRuleConfigReader type. Tried 'ValidationFramework.Configuration.InvalidReaderConfigReader,ValidationFramework'.")]
			public void ExceptionNotExistNoCommas()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' typeName='InvalidReader'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not load IRuleConfigReader type. Tried 'AssemblyName,InvalidReader'.")]
			public void ExceptionNotExistCommas()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' typeName='AssemblyName,InvalidReader'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
			}
#if (WindowsCE)
			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not load IRuleConfigReader type. Tried 'CompactValidationFramework.Tests.Configuration.NoConfigReaderRuleConfigReader,ValidationFramework.Tests, Version=1.1.0.0, Culture=neutral, PublicKeyToken=2f07bd240683f08a'.")]
			public void ExceptionNoConfigReader()
			{
				const string validatorXml = "<rule validationExpression='aaaaa' typeName='ValidationFramework.Tests.Configuration.NoConfigReaderRule,CompactValidationFramework.Tests'/>";
            var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
			}

#else
            [Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not load IRuleConfigReader type. Tried 'ValidationFramework.Tests.Configuration.NoConfigReaderRuleConfigReader,ValidationFramework.Tests, Version=1.1.0.0, Culture=neutral, PublicKeyToken=2f07bd240683f08a'.")]
			public void ExceptionNoConfigReader()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' typeName='ValidationFramework.Tests.Configuration.NoConfigReaderRule,ValidationFramework.Tests'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
			}

#endif

			[Test]
			public void Simple1()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' errorMessage='hello' typeName='ValidationFramework.Configuration.RegexRuleConfigReader,ValidationFramework' ruleSet='foo' regexOptions='RightToLeft'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				var rule = (RegexRule)ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
				Assert.AreEqual("hello", rule.ErrorMessage);
				Assert.AreEqual(RegexOptions.RightToLeft, rule.RegexOptions);
				Assert.AreEqual("aaaaa", rule.ValidationExpression);
			}
			[Test]
			public void Simple2()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' errorMessage='hello' typeName='RegexRuleConfigReader' ruleSet='foo' regexOptions='RightToLeft'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				var rule = (RegexRule)ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
				Assert.AreEqual("hello", rule.ErrorMessage);
				Assert.AreEqual(RegexOptions.RightToLeft, rule.RegexOptions);
				Assert.AreEqual("aaaaa", rule.ValidationExpression);
			}
			[Test]
			public void Simple3()
			{
                const string validatorXml = "<rule validationExpression='aaaaa' errorMessage='hello' typeName='RegexRule' ruleSet='foo' regexOptions='RightToLeft'/>";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				var rule = (RegexRule)ConfigurationService.GetRule(ruleData, TypePointers.StringTypeHandle);
				Assert.AreEqual("hello", rule.ErrorMessage);
				Assert.AreEqual(RegexOptions.RightToLeft, rule.RegexOptions);
				Assert.AreEqual("aaaaa", rule.ValidationExpression);
			}

			[Test]
			public void Simple4()
			{
                const string validatorXml = "<rule typeName='ValidationFramework.Tests.MockRule,ValidationFramework.Tests' />";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				var rule = (MockRule)ConfigurationService.GetRule(ruleData, typeof(string).TypeHandle);
			}
			[Test]
			public void Simple5()
			{
                const string validatorXml = "<rule typeName='ValidationFramework.Tests.MockRuleConfigReader,ValidationFramework.Tests' />";
                var ruleData = (RuleData.Read(validatorXml.ToXmlReader()));
				var rule = (MockRule)ConfigurationService.GetRule(ruleData, typeof(string).TypeHandle);
			}
		}
	}
}